﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

using PickGold.Handler;
using PickGold.Arguments;
using PickGold.Exceptions;

using CAsyncResultDictionary = System.Collections.Generic.Dictionary<object, PickGold.Callers.CRemoteProcedureCallAsyncResult>;

namespace PickGold.Callers
{
	/// <summary>
	/// Remote Procedure Call
	/// </summary>
	public class CRemoteProcedureCall : IRemoteProcedureCall
	{
		private static CRemoteProcedureCall _Value;
		private static int _DefaultTimeout = 60000;//60 * 1000 = 1 Minute
		private static CAsyncResultDictionary _AsyncResultDictionary = new CAsyncResultDictionary();

		private int _Timeout;
		private object _Tag;

		/// <summary>
		/// 默认的委托调用委托
		/// </summary>
		public static HAsyncCallHandler DefaultInvoke;

		/// <summary>
		/// 默认的超时委托
		/// </summary>
		public static event HRpcTimeoutEventHandler DefaultInvokeTimeout;

		/// <summary>
		/// 委托调用委托
		/// </summary>
		public HAsyncCallHandler Invoke;

		/// <summary>
		/// 超时委托
		/// </summary>
		public event HRpcTimeoutEventHandler InvokeTimeout;

		/*
		/// <summary>
		/// 实例化一RPC实例
		/// </summary>
		public CRemoteProcedureCall()
		{
			this.Invoke = CRemoteProcedureCall.DefaultInvoke;
			this.Timeout = CRemoteProcedureCall.DefaultTimeout;
			this.InvokeTimeout = CRemoteProcedureCall.DefaultInvokeTimeout;
		}
		*/

		/// <summary>
		/// 默认的RPC实例
		/// </summary>
		public static CRemoteProcedureCall Value
		{
			set
			{
				CRemoteProcedureCall._Value = value;
			}
			get
			{
				if (CRemoteProcedureCall._Value == null)
					CRemoteProcedureCall._Value = new CRemoteProcedureCall();
				return CRemoteProcedureCall._Value;
			}
		}

		/// <summary>
		/// 异步委托调用对象，一般为窗口控件类
		/// </summary>
		public static IAsyncInvoker AsyncInvoker
		{
			set
			{
				Common.AsyncInvoker = value;
			}
			get
			{
				return Common.AsyncInvoker;
			}
		}

		/// <summary>
		/// 获取或设置默认的超时时间（毫秒）
		/// </summary>
		public static int DefaultTimeout
		{
			get
			{
				return CRemoteProcedureCall._DefaultTimeout;
			}
			set
			{
				CRemoteProcedureCall._DefaultTimeout = value;
			}
		}

		/// <summary>
		/// 获取或设置超时时间（毫秒）
		/// </summary>
		public int Timeout
		{
			get
			{
				return this._Timeout;
			}
			set
			{
				this._Timeout = value;
			}
		}

		/// <summary>
		/// 附加的其它数据
		/// </summary>
		public object Tag
		{
			set
			{
				this._Tag = value;
			}
			get
			{
				return this._Tag;
			}
		}

		/// <summary>
		/// 开始一个默认的异步委托调用
		/// </summary>
		/// <param name="handler">异步调用委托</param>
		/// <param name="callback">异步调用完成委托</param>
		/// <returns>异步操作状态</returns>
		public static IAsyncResult BeginDefaultInvoke<TYPE>(IDelegate<TYPE> handler, AsyncCallback callback)
		{
			return CRemoteProcedureCall.BeginDefaultInvoke<TYPE>(handler, callback, handler);
		}

		/// <summary>
		/// 开始一个默认的异步委托调用
		/// </summary>
		/// <param name="handler">异步调用委托</param>
		/// <param name="callback">异步调用完成委托</param>
		/// <param name="object">自定义操作状态</param>
		/// <returns>异步操作状态</returns>
		public static IAsyncResult BeginDefaultInvoke<TYPE>(IDelegate<TYPE> handler, AsyncCallback callback, object @object)
		{
			if (CRemoteProcedureCall.DefaultInvoke == null)
				return null;

			CAsyncCallEventArgs e = new CAsyncCallEventArgs(handler);
			e.InnerResult = new CRemoteProcedureCallAsyncResult(callback, @object);
			e.Timeout = CRemoteProcedureCall.DefaultTimeout;
			CRemoteProcedureCall.DefaultInvoke(null, e);

			if (e.InnerAsyncKey == null)
				return null;

			if (e.Timeout > 0)
			{
				//ThreadPool.QueueUserWorkItem(new WaitCallback(
				new Thread(new ParameterizedThreadStart(CRemoteProcedureCall.TimeoutThread)).Start(new object[] {
						e.InnerAsyncKey,//0
						e.InnerTimeout,//1
						e.InnerResult.InnerWaitHandle,//2
						CRemoteProcedureCall.DefaultInvokeTimeout//3
				});
				//new HNoReturnDelegate<object, int, WaitHandle, HRpcTimeoutEventHandler>(CRemoteProcedureCall.TimeoutThread).BeginInvoke(e.InnerAsyncKey, e.InnerTimeout, e.InnerResult.InnerWaitHandle, CRemoteProcedureCall.DefaultInvokeTimeout, CRepeatRunner.InvokeAsyncCallback, null);
			}
			return e.InnerResult;
		}

		/// <summary>
		/// 开始一个异步委托调用
		/// </summary>
		/// <param name="handler">异步调用委托</param>
		/// <param name="callback">异步调用完成委托</param>
		/// <returns>异步操作状态</returns>
		public IAsyncResult BeginInvoke<TYPE>(IDelegate<TYPE> handler, AsyncCallback callback)
		{
			return this.BeginInvoke<TYPE>(handler, callback, handler);
		}

		/// <summary>
		/// 开始一个异步委托调用
		/// </summary>
		/// <param name="handler">异步调用委托</param>
		/// <param name="callback">异步调用完成委托</param>
		/// <param name="object">自定义操作状态</param>
		/// <returns>异步操作状态</returns>
		public IAsyncResult BeginInvoke<TYPE>(IDelegate<TYPE> handler, AsyncCallback callback, object @object)
		{
			if (this.Invoke == null && CRemoteProcedureCall.DefaultInvoke == null)
				return null;

			CAsyncCallEventArgs e = new CAsyncCallEventArgs(handler);
			e.InnerResult = new CRemoteProcedureCallAsyncResult(callback, @object);
			e.Timeout = this.Timeout;
			if (this.Invoke != null)
				this.Invoke(this, e);
			else if (CRemoteProcedureCall.DefaultInvoke != null)
				CRemoteProcedureCall.DefaultInvoke(this, e);

			if (e.InnerAsyncKey == null)
				return null;

			if (e.InnerTimeout > 0)
			{
				new Thread(new ParameterizedThreadStart(CRemoteProcedureCall.TimeoutThread)).Start(new object[] {
					e.InnerAsyncKey,
					e.InnerTimeout,
					e.InnerResult.InnerWaitHandle,
					this.InvokeTimeout
				});
				//new HNoReturnDelegate<object, int, WaitHandle, HRpcTimeoutEventHandler>(CRemoteProcedureCall.TimeoutThread).BeginInvoke(e.InnerAsyncKey, e.InnerTimeout, e.InnerResult.InnerWaitHandle, this.InvokeTimeout, CRepeatRunner.InvokeAsyncCallback, null);
			}
			return e.InnerResult;
		}

		/// <summary>
		/// 导步委托调用结束
		/// </summary>
		/// <param name="asyncResult">异步操作状态</param>
		/// <param name="result">异步操作结果</param>
		/// <returns>调用异常，如果成功则返回空</returns>
		public static Exception EndInvoke<TYPE>(IAsyncResult asyncResult, ref TYPE result)
		{
			CRemoteProcedureCallAsyncResult ar = asyncResult as CRemoteProcedureCallAsyncResult;
			if (ar == null)
				return new ArgumentOutOfRangeException("asyncResult", "异步操作状态不正确！");

			if (ar.Error != null)
				return ar.Error;

			result = (TYPE)ar.Result;
			return null;
		}

		/// <summary>
		/// 发起回调
		/// </summary>
		/// <param name="asyncKey">异步操作状态键</param>
		/// <param name="result">结果</param>
		public static void RaiseCallbackResult(object asyncKey, object result)
		{
			if (asyncKey == null)
				return;

			CRemoteProcedureCallAsyncResult ar;
			if (!Monitor.TryEnter(CRemoteProcedureCall._AsyncResultDictionary, Fiber.LockTimeout))
				Fiber.ThrowLockTimeoutException(CRemoteProcedureCall._AsyncResultDictionary);
			try
			{
				if (!CRemoteProcedureCall._AsyncResultDictionary.ContainsKey(asyncKey))
					return;

				ar = CRemoteProcedureCall._AsyncResultDictionary[asyncKey];
				CRemoteProcedureCall._AsyncResultDictionary.Remove(asyncKey);
			}
			finally
			{
				Monitor.Exit(CRemoteProcedureCall._AsyncResultDictionary);
			}
			ar.Result = result;
			ar.InnerCompleted = true;
			ar.InnerWaitHandle.Set();
			if (ar.InnerThread == Thread.CurrentThread)
			{
				ar.Callback(ar);
				return;
			}

			if (CRemoteProcedureCall.AsyncInvoker != null && CRemoteProcedureCall.AsyncInvoker.InvokeRequired)
			{
				CRemoteProcedureCall.AsyncInvoker.Invoke(ar.Callback, ar);
				return;
			}

			ar.Callback(ar);
		}

		/// <summary>
		/// 发起异常回调
		/// </summary>
		/// <param name="asyncKey">异步操作状态键</param>
		/// <param name="error">异常</param>
		public static void RaiseCallbackError(object asyncKey, Exception error)
		{
			if (asyncKey == null)
				return;

			CRemoteProcedureCallAsyncResult ar;
			if (!Monitor.TryEnter(CRemoteProcedureCall._AsyncResultDictionary, Fiber.LockTimeout))
				Fiber.ThrowLockTimeoutException(CRemoteProcedureCall._AsyncResultDictionary);
			try
			{
				if (!CRemoteProcedureCall._AsyncResultDictionary.ContainsKey(asyncKey))
					return;

				ar = CRemoteProcedureCall._AsyncResultDictionary[asyncKey];
				CRemoteProcedureCall._AsyncResultDictionary.Remove(asyncKey);
			}
			finally
			{
				Monitor.Exit(CRemoteProcedureCall._AsyncResultDictionary);
			}
			ar.Error = error;
			ar.InnerCompleted = true;
			ar.InnerWaitHandle.Set();
			if (ar.InnerThread == Thread.CurrentThread)
			{
				ar.Callback(ar);
				return;
			}

			if (CRemoteProcedureCall.AsyncInvoker != null && CRemoteProcedureCall.AsyncInvoker.InvokeRequired)
			{
				CRemoteProcedureCall.AsyncInvoker.Invoke(ar.Callback, ar);
				return;
			}

			ar.Callback(ar);
		}

		/// <summary>
		/// 指定的键中是否在执行
		/// </summary>
		/// <param name="asyncKey"></param>
		/// <returns></returns>
		public static bool HasInvoke(object asyncKey)
		{
			if (asyncKey == null)
				return true;

			if (!Monitor.TryEnter(CRemoteProcedureCall._AsyncResultDictionary, Fiber.LockTimeout))
				Fiber.ThrowLockTimeoutException(CRemoteProcedureCall._AsyncResultDictionary);
			try
			{
				return CRemoteProcedureCall._AsyncResultDictionary.ContainsKey(asyncKey);
			}
			finally
			{
				Monitor.Exit(CRemoteProcedureCall._AsyncResultDictionary);
			}
		}

		/// <summary>
		/// 委托异步操作状态键设置
		/// </summary>
		internal static void SetInvokeKey(CAsyncCallEventArgs e, object asyncKey)
		{
			if (!Monitor.TryEnter(CRemoteProcedureCall._AsyncResultDictionary, Fiber.LockTimeout))
				Fiber.ThrowLockTimeoutException(CRemoteProcedureCall._AsyncResultDictionary);
			try
			{
				if (asyncKey != null)
					CRemoteProcedureCall._AsyncResultDictionary[asyncKey] = e.InnerResult;
				if (e.InnerAsyncKey == null)
					return;

				if (CRemoteProcedureCall._AsyncResultDictionary.ContainsKey(e.InnerAsyncKey))
					CRemoteProcedureCall._AsyncResultDictionary.Remove(e.InnerAsyncKey);
			}
			finally
			{
				Monitor.Exit(CRemoteProcedureCall._AsyncResultDictionary);
			}
		}

		private static void TimeoutThread(object args)//object[]{asyncKey,timeout,waitHandler,invokeTimeoutHandler}
		{
			CRemoteProcedureCallAsyncResult result;
			object[] os = args as object[];
			object asyncKey = os[0];
			if (asyncKey == null)
				return;

			int timeout = (int)os[1];
			if (timeout <= 0)
				timeout = CRemoteProcedureCall._DefaultTimeout;
			if (timeout <= 0)
				return;

			var waitHandle = (WaitHandle)os[2];
			var invokeTimeout = (HRpcTimeoutEventHandler)os[3];
			try//Thread.Sleep(timeout);
			{
				if (waitHandle.WaitOne(timeout))
					return;
			}
			catch
			{
				return;
			}

			if (!Monitor.TryEnter(CRemoteProcedureCall._AsyncResultDictionary, Fiber.LockTimeout))
				Fiber.ThrowLockTimeoutException(CRemoteProcedureCall._AsyncResultDictionary);
			try
			{
				if (!CRemoteProcedureCall._AsyncResultDictionary.ContainsKey(asyncKey))
					return;

				result = CRemoteProcedureCall._AsyncResultDictionary[asyncKey];
			}
			finally
			{
				Monitor.Exit(CRemoteProcedureCall._AsyncResultDictionary);
			}
			if (invokeTimeout == null)
				invokeTimeout = CRemoteProcedureCall.DefaultInvokeTimeout;
			if (invokeTimeout != null)
			{
				var e = new CRpcTimeoutEventArgs(result, result.StartTime);
				e.Timeout = timeout;
				do
				{
					if (result.InnerThread == Thread.CurrentThread)
						invokeTimeout(result, e);
					else if (CRemoteProcedureCall.AsyncInvoker != null && CRemoteProcedureCall.AsyncInvoker.InvokeRequired)
						CRemoteProcedureCall.AsyncInvoker.Invoke(invokeTimeout, result, e);
					else
						invokeTimeout(result, e);
					if (!Monitor.TryEnter(CRemoteProcedureCall._AsyncResultDictionary, Fiber.LockTimeout))
						Fiber.ThrowLockTimeoutException(CRemoteProcedureCall._AsyncResultDictionary);
					try
					{
						if (!CRemoteProcedureCall._AsyncResultDictionary.ContainsKey(asyncKey))
							return;
					}
					finally
					{
						Monitor.Exit(CRemoteProcedureCall._AsyncResultDictionary);
					}
					if (result.IsCompleted)
						return;

					if (!e.Continue)
						break;

					if (e.Timeout <= 0)
						return;

					try//Thread.Sleep(e.Timeout);
					{
						if (waitHandle.WaitOne(e.Timeout))
							return;
					}
					catch
					{
						return;
					}
				}
				while (e.Continue);
			}

			RpcTimeoutException exception = new RpcTimeoutException(result, "The remote procedure call timeout.");
			CRemoteProcedureCall.RaiseCallbackError(result, exception);
		}

		Exception IRemoteProcedureCall.EndInvoke<TYPE>(IAsyncResult asyncResult, ref TYPE result)
		{
			return CRemoteProcedureCall.EndInvoke<TYPE>(asyncResult, ref result);
		}
	}
}
